package cli.command;

import cli.view.LoadingIcon;

import java.io.*;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

// 负责文件和文件夹的压缩和解压
public class Zip implements Command {
    private PrintWriter out;
    private Path currentPath;
    private boolean c = false; // 是否压缩
    private boolean x = false; // 是否解压
    private boolean f = false; // 是否指定dst文件路径
    private String dst = null; // 压缩或解压之后的文件路径
    private String src = null; // 压缩或解压之前的文件路径

    public Zip(PrintWriter out, Path currentPath) {
        this.out = out;
        this.currentPath = currentPath;
    }

    @Override
    public void handle(String[] args) throws Exception {
        accept(args);
        execute();
    }

    // 解析参数
    private void accept(String[] args) {
        int pos = 0;
        if (pos < args.length) {
            if (args[pos].startsWith("-")) {
                c = args[pos].contains("c");
                x = args[pos].contains("x");
                f = args[pos].contains("f");
            } else {
                src = args[pos];
            }
            pos++;
        }
        if (f) {
            if (pos < args.length) {
                dst = args[pos++];
            }
        }
        if (src == null) {
            if (pos < args.length) {
                src = args[pos++];
            }
        }
    }

    // 执行命令
    private void execute() {
        Path src;
        if (this.src == null) {
            src = currentPath;
        } else {
            src = currentPath.resolve(this.src);
        }
        Path dst;
        if (this.dst == null) {
            if (c) {
                dst = currentPath.resolve(src.getFileName().toString() + ".zip");
            } else if (x) {
                dst = currentPath.resolve(src.getFileName().toString().replace(".zip", ""));
            } else {
                return;
            }
        } else {
            dst = currentPath.resolve(this.dst);
        }
        if (c) {
            LoadingIcon.wait(out, () -> zip(src, dst));
        } else if (x) {
            LoadingIcon.wait(out, () -> unzip(src, dst));
        }
    }

    // 压缩
    private static void zip(Path src, Path dst) {
        try (OutputStream fos = Files.newOutputStream(dst, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE)) {
            ZipOutputStream zos = new ZipOutputStream(fos);
            Files.walk(src).filter(Files::isRegularFile).forEach(p -> {
                try (InputStream fis = Files.newInputStream(p, StandardOpenOption.READ)) {
                    ZipEntry e = new ZipEntry(src.relativize(p).toString());
                    zos.putNextEntry(e);
                    byte[] buffer = new byte[1024];
                    int bytesRead;
                    while ((bytesRead = fis.read(buffer)) != -1) {
                        zos.write(buffer, 0, bytesRead);
                    }
                    zos.closeEntry();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 解压
    private static void unzip(Path src, Path dst) {
        try (InputStream fis = Files.newInputStream(src, StandardOpenOption.READ)) {
            ZipInputStream zis = new ZipInputStream(fis);
            byte[] buffer = new byte[1024];
            for (ZipEntry e = zis.getNextEntry(); e != null; e = zis.getNextEntry()) {
                Path p = dst.resolve(e.getName());
                Files.createDirectories(p.getParent());
                try (OutputStream fos = Files.newOutputStream(p, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE)) {
                    int bytesRead;
                    while ((bytesRead = zis.read(buffer)) != -1) {
                        fos.write(buffer, 0, bytesRead);
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
